package de.cinovo.cloudconductor.server.util; import java.math.BigInteger; import java.security.SecureRandom; import java.util.concurrent.ThreadLocalRandom; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import de.cinovo.cloudconductor.server.dao.IAgentAuthTokenDAO; import de.cinovo.cloudconductor.server.model.EAgentAuthToken; import de.cinovo.cloudconductor.server.util.exception.TokenGenerationException; /** * Copyright 2016 Cinovo AG<br> * <br> * * @author ablehm * */ @Service public class AuthTokenGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(AuthTokenGenerator.class); @Autowired private IAgentAuthTokenDAO dToken; /** * Generates a unique AuthToken and saves it in the database. * * @param tokenLength the length of the token to generate * * @return a generated AuthToken if generated successful, or null if something went wrong * @throws TokenGenerationException - when generation of unqiue token failed in loop after 10 attempts */ public EAgentAuthToken generateAuthToken(int tokenLength) throws TokenGenerationException { String generatedToken = null; int count = 0; while (!this.dToken.isTokenUnique(generatedToken)) { if (count > 10) { String errorMsg = "Failed to generate unique token."; AuthTokenGenerator.LOGGER.error(errorMsg); throw new TokenGenerationException(errorMsg); } count++; generatedToken = this.generateToken(tokenLength); } EAgentAuthToken token = new EAgentAuthToken(); token.setToken(generatedToken); token.setCreationDate(new DateTime().getMillis()); token = this.dToken.save(token); return token; } protected String generateToken(int tokenLength) { String generatedToken = new BigInteger(tokenLength * 5, new SecureRandom()).toString(32); generatedToken = this.generatePartialUppercasedToken(tokenLength, generatedToken); return this.shuffleWithFisherYates(generatedToken); } private String generatePartialUppercasedToken(int tokenLength, String currentToken) { StringBuilder tokenStringToShuffle = new StringBuilder(); tokenStringToShuffle.append(currentToken.substring(0, tokenLength / 2).toUpperCase()); tokenStringToShuffle.append(currentToken.substring(tokenLength / 2, tokenLength)); return tokenStringToShuffle.toString(); } private String shuffleWithFisherYates(String tokenStringToShuffle) { char[] shuffleArray = tokenStringToShuffle.toCharArray(); int tokenLength = tokenStringToShuffle.length(); for (int i = 0; i < (tokenLength - 2); i++) { int j = ThreadLocalRandom.current().nextInt(i, tokenLength); shuffleArray = this.swap(shuffleArray, i, j); } return new String(shuffleArray); } private char[] swap(char[] toSwapIn, int i, int j) { char[] toReturn = toSwapIn; char temp = toReturn[i]; toReturn[i] = toReturn[j]; toReturn[j] = temp; return toReturn; } }